package com.lecoboy.handwritespring.spring.framework.context;

import com.lecoboy.handwritespring.spring.framework.annotation.MyAutoWirted;
import com.lecoboy.handwritespring.spring.framework.annotation.MyController;
import com.lecoboy.handwritespring.spring.framework.annotation.MyServices;
import com.lecoboy.handwritespring.spring.framework.beans.MyBeanWrapper;
import com.lecoboy.handwritespring.spring.framework.beans.config.MyBeanDefinition;
import com.lecoboy.handwritespring.spring.framework.beans.support.MyBeanDefinitionReader;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 *
 */
public class MyApplicationContext {

    private final Map<String, MyBeanDefinition> beanDefinitionMap = new HashMap<String, MyBeanDefinition>();
    //真实IOC容器
    private final Map<String, MyBeanWrapper> factoryBeanInstanceCache = new HashMap<String, MyBeanWrapper>();
    private final Map<String, Object> factoryBeanObjectCache = new HashMap<String, Object>();
    private String[] configLocations;

    private MyBeanDefinitionReader reader;

    public MyApplicationContext(String... configLocations) {
        try {

            //拿配置
            this.configLocations = configLocations;
            //1.读取配置
            reader = new MyBeanDefinitionReader(this.configLocations);
            //2.解析配置文件，封装成BeanDefinition
            List<MyBeanDefinition> beanDefinitions = reader.loadBeanDefinitions();
            //3.把BeanDefinition对应的实例放入到IOC容器
            doRegisterBeanDefinition(beanDefinitions);

            //初始化阶段完成

            //4.完成依赖注入
            doAutowrited();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 依赖注入（DI）
     */
    private void doAutowrited() {
        for (Map.Entry<String, MyBeanDefinition> beanDefinitionEntry : this.beanDefinitionMap.entrySet()) {
            String beanName = beanDefinitionEntry.getKey();
            getBean(beanName);
        }
    }


    public Object getBean(String beanName) {
        //1.读取bean信息
        MyBeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
        //2.反射实例化
        Object instance = instantiateBean(beanName, beanDefinition);
        //3.把创建出来的真实实例包装为BeanWrapper对象
        MyBeanWrapper beanWrapper = new MyBeanWrapper(instance);
        //4.把BeanWrapper对象放入真正的IOC容器里面
        this.factoryBeanInstanceCache.put(beanName, beanWrapper);
        //5.执行依赖注入（DI）
        populateBean(beanName, new MyBeanDefinition(), beanWrapper);

        return this.factoryBeanInstanceCache.get(beanName).getWrapperInstance();
    }

    private void populateBean(String beanName, MyBeanDefinition myBeanDefinition, MyBeanWrapper beanWrapper) {

        Object instance = beanWrapper.getWrapperInstance();
        Class<?> clazz = beanWrapper.getWrapperClass();

        //只有加了注解的才进行依赖注入
        if (!(clazz.isAnnotationPresent(MyController.class) || clazz.isAnnotationPresent(MyServices.class))) {
            return;
        }
        //拿到实例的所有字段
        //Declared 所有的，特定的 字段，包括privave/protected/default
        //正常来说，普通的OOP编程只能拿到public的属性
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (!field.isAnnotationPresent(MyAutoWirted.class)) {
                continue;
            }
            MyAutoWirted autoWirted = field.getAnnotation(MyAutoWirted.class);
            //如果用户没有自定义beanName，默认就根据类型注入
            String autowiredBeanName = autoWirted.value().trim();
            if ("".equals(autowiredBeanName)) {
                //获得接口的类型，拿这个key到ioc容器中取值
                autowiredBeanName = field.getType().getName();
            }

            //如果是public以外的修饰符，只要加了@Autowired注解，都要强制赋值
            //反射中叫暴力访问，强吻
            field.setAccessible(true);

            //反射调用的方式
            //给entry.getValue这个对象的field字段，赋值ioc.get(beanName)

            if (this.factoryBeanInstanceCache.get(autowiredBeanName) == null) {
                continue;
            }

            try {
                Object beanInstance = this.factoryBeanInstanceCache.get(beanName).getWrapperInstance();
                field.set(instance, beanInstance);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
                continue;
            }
        }
    }


    private Object instantiateBean(String beanName, MyBeanDefinition beanDefinition) {

        String className = beanDefinition.getBeanClassName();

        Object instance = null;
        try {
            Class<?> clazz = Class.forName(className);
            instance = clazz.newInstance();

            //TODO 未完
            /*//1、读取通知和目标类的关联关系
            GPAdvisedSupport config = instantionAopConfig(gpBeanDefinition);
            config.setTargetClass(clazz);
            config.setTarget(instance);
            //2、生成一个代理类
            //不是所有的类都会生成代理类
            if(config.pointCutMatch()) {
                instance = new GPJdkDynamicAopProxy(config).getProxy();
            }
            this.factoryBeanObjectCache.put(beanName,instance);
*/

        } catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }

    /**
     * 存入IOC容器
     *
     * @param beanDefinitions
     */
    private void doRegisterBeanDefinition(List<MyBeanDefinition> beanDefinitions) throws Exception {
        for (MyBeanDefinition beanDefinition : beanDefinitions) {
            if (this.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())) {
                throw new Exception("The " + beanDefinition.getFactoryBeanName() + " is exists");
            }
            this.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(), beanDefinition);
        }
    }

    /**
     * 获取所有自定义BeanNames
     * @return
     */
    public String[] getBeanDefinitionNames() {
        return beanDefinitionMap.keySet().toArray(new String[beanDefinitionMap.size()]);
    }

    public Properties getConfig(){
        return this.reader.getConfig();
    }
}
